package com.cnd.scm.jdbcprocessor.helper;

import com.cnd.scm.common.utils.CUtils;
import com.cnd.scm.common.utils.ConvertUtils;
import com.cnd.scm.common.utils.ReflectionUtils;
import com.cnd.scm.jdbcprocessor.constant.CommandType;
import com.cnd.scm.jdbcprocessor.constant.DataBaseType;
import com.cnd.scm.jdbcprocessor.constant.ResolvedCodeType;
import com.cnd.scm.jdbcprocessor.dbentity.*;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 该类作为连接数据库访问操作的基础工具类
 * 封装了对数据库查询 新增 修改 执行存储过程的通用方法
 * 该设计思想来源于.NET平台的SqlHelper而构建
 * @author 李启联
 * @version 1.1
 * 
 * 说明： 该数据库访问工具类 可以完美与Spring框架集成 只要在Spring的配置文件中加入如下配置便可
 *     1.SpringMVC 配置
 *     <bean id="jdbcHelper" class="com.phoenix.jsef.jdbcutils.SqlHelper">
 *   	<property name="dataSource" ref="dataSource" />
 *   	<property name="dbParamterPrefix" value="V_" />
 *     </bean>
 *
 *     2.SpringBoot 配置,以阿里的Druid数据源配合为例
 *      @Configuration
 *      @ConfigurationProperties("spring.datasource")
 *      @AutoConfigureAfter(value= {DruidDataSourceConfig.class})
 *      public class DBConnectionConfig {
 *
 *      	@Autowired
 *      	@Qualifier("druidDataSource")
 *      	private DataSource dataSource;
 *
 *      	private String dbParamterPrefix = "";
 *
 *      	public String getDbParamterPrefix() {
 *      		return dbParamterPrefix;
 *      	}
 *      	public void setDbParamterPrefix(String dbParamterPrefix) {
 *      		this.dbParamterPrefix = dbParamterPrefix;
 *      	}
 *      	@Bean(name="sqlHelper")
 *      	public SqlHelper sqlHelper() {
 *      		SqlHelper sqlHelper = new SqlHelper();
 *              sqlHelper.setDataSource(dataSource);
 *              sqlHelper.setDbParamterPrefix(dbParamterPrefix);
 *      		return sqlHelper;
 *      	}
 *
 *      }
 *
 * 
 */
@Slf4j
public class SqlHelper {
	
	public SqlHelper() {
		System.out.println("SqlHelper Create...");
	}
	
	public SqlHelper(DataSource dataSource, String dbParamterPrefix) {
		this.dataSource =  dataSource;
		this.dbParamterPrefix = dbParamterPrefix;
	}

    public SqlHelper(DataSource dataSource, String dbParamterPrefix, String sqlFilePrefix, String sqlFileSuffix) {
		this.dataSource =  dataSource;
		this.dbParamterPrefix = dbParamterPrefix;
		this.sqlFilePrefix = sqlFilePrefix;
		this.sqlFileSuffix = sqlFileSuffix;
    }
	

	private  ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

	private  DataSource dataSource = null; //数据源	
	/**
	 * 得到一个数据源
	 * @return
	 */
	public  DataSource getDataSource() {
		return dataSource;
	}
	/**
	 * 设置数据源
	 * @param dataSource
	 */
	public  void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	
	//数据库中参数定义使用的自定义前缀  如在MSSQL中无需自定义前缀， 因为其本身定义了"@"为前缀  ; 在mysql中我们习惯使用"V_" 或"P_"	为前缀
	//这属性很重要涉及到后续调用数据库中存储过程（忽略参数时候）参数的匹配
	private  String dbParamterPrefix = "";
	/**
	 *得到数据库中参数定义时使用的自定义前缀 ，如果有使用前缀则需要在配置中指定说明
	 *如：<property name="dbParamterPrefix" value="V_" ></property>
	 * @return
	 */
	public  String getDbParamterPrefix() {
		return dbParamterPrefix;
	}
	public  void setDbParamterPrefix(String dbParamterPrefix) {
		this.dbParamterPrefix = dbParamterPrefix;
	}

    //定义待执行的sql文件名的前缀
    private  String sqlFilePrefix = "classpath:/sqlscript/";
    //定义待执行的sql文件名的后缀
    private  String sqlFileSuffix = ".sql";

    public  String getSqlFilePrefix() {
        return sqlFilePrefix;
    }
    public  void setSqlFilePrefix(String sqlFilePrefix) {
		this.sqlFilePrefix = sqlFilePrefix;
    }

    public  String getSqlFileSuffix() {
        return sqlFileSuffix;
    }
    public  void setSqlFileSuffix(String sqlFileSuffix) {
		this.sqlFileSuffix = sqlFileSuffix;
    }


	/**
	 * 从数据库连接池中得到一个连接
	 * 此连接在同一线程内共享
	 * @return
	 * @throws SQLException
	 */
	public  Connection getConnection() throws SQLException {
		Connection conn = tl.get();
		if (conn == null) {
			conn = this.getDataSource().getConnection();
			tl.set(conn);
		}
		return conn;
	}

	/**
	 * 开始一个事务
	 * 
	 */
	public  void beginTransaction() {
		try {
			Connection conn = getConnection();
			if (conn != null) {
				//默认conn是自动提交
				if (conn.getAutoCommit()){ 
					//关闭自动提交，即是手动开启事务  
					conn.setAutoCommit(false);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 提交一个事务
	 * 
	 * @throws SQLException
	 * 
	 */
	public  void commitTransaction() throws SQLException {
		Connection conn = getConnection();
		try {
			if (conn != null) {
				if(!conn.getAutoCommit()){
					conn.commit();
					conn.setAutoCommit(true);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			if (conn != null && !conn.isClosed()) {
				conn.close();
			}
			tl.remove();
		}
	}

	/**
	 * 回滚一个事务
	 * 
	 * @throws SQLException
	 * 
	 */
	public  void rollBackTransaction() throws SQLException {
		Connection conn = getConnection();
		try {
			if (conn != null) {
				if(!conn.getAutoCommit()){
					conn.rollback();
					conn.setAutoCommit(true);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			if (conn != null && !conn.isClosed()) {
				conn.close();
			}
			tl.remove();
		}
	}

	/**
	 * 得到当前连接的数据库类型
	 * @return 数据库类型
	 * @throws Exception
	 */
	public  DataBaseType getDataBaseType() throws Exception {
		String dataBaseType = getConnection().getMetaData().getDatabaseProductName();
		if(dataBaseType.toUpperCase().contains("MYSQL"))
			return Enum.valueOf(DataBaseType.class, "MYSQL");
		else if(dataBaseType.toUpperCase().contains("MICROSOFT SQL SERVER"))
			return Enum.valueOf(DataBaseType.class, "SQLSERVER");
		else if(dataBaseType.toUpperCase().contains("ORACLE"))
			return Enum.valueOf(DataBaseType.class, "ORACLE");
		else
			return Enum.valueOf(DataBaseType.class, "UNKNOWN");
	}
	/**
	 * 通过SQL查询语句或存储过程得到数据集合列表<br/>
	 * 由于该方式执行sql不够安全，后续增加了queryForList这方法 可避免SQL注入
	 * @param conn
	 *            数据库连接对象
	 * @param commandType
	 *            执行类型（SQL语句执行或调用存储过程执行）
	 * @param cmdText
	 *            SQL语句或存储过程名称 ,SQL语句形式：拼凑的SQL语句(select * from T where Id=@Id )
	 * @param obj
	 *           执行存储过程时传入的可变参数
	 * @return （数据集合列表）返回：List<Map<String,Object>>
	 * @throws SQLException
	 */
    @Deprecated
	public  List getResultList(Connection conn, CommandType commandType, String cmdText, Object... obj) throws Exception {
		Statement sm = null;
		CallableStatement cs = null;
		ResultSet resultSet = null;
		List list = null;

		try {
			if (commandType.equals(CommandType.Text)) {
				sm = conn.createStatement();
				resultSet = sm.executeQuery(cmdText);
			} else if (commandType.equals(CommandType.StoredProcedure)) {
                cs =  prepareCallableStatement(conn,cmdText,obj);
				resultSet = cs.executeQuery();
			}

			list = resultSetToList(resultSet);
            log.info("执行数据库操作 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{}","getResultList",conn,commandType,cmdText, obj);
		} catch (Exception e) {
			e.printStackTrace();
            log.error("执行数据库操作异常:{} 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{}",e.toString(),"getResultList",conn,commandType,cmdText, obj);
		} finally {
			close(conn, null, sm, cs, resultSet);

		}
		return list;
	}

	/**
	 * 通过SQL查询语句或存储过程得到数据集合列表<br/>
	 * 由于该方式执行sql不够安全，后续增加了queryForList这方法 可避免SQL注入
	 * @param commandType
	 *            执行类型（SQL语句执行或调用存储过程执行）
	 * @param cmdText
	 *            SQL语句或存储过程名称 ,SQL语句形式：拼凑的SQL语句(select * from T where Id=@Id )
	 * @param obj
	 *            执行存储过程时传入的可变参数
	 * @return （数据集合列表）返回：List<Map<String,Object>>
	 * @throws SQLException
	 * @throws Exception
	 */
    @Deprecated
	public  List getResultList(CommandType commandType, String cmdText, Object... obj)
			throws SQLException, Exception {
		return getResultList(getConnection(), commandType, cmdText, obj);
	}

	/**
	 * 通过预编译的SQL查询语句（防SQL注入）或存储过程得到数据集合列表
	 * 
	 * @param conn
	 *            数据库连接对象
	 * @param commandType
	 *            执行类型（SQL语句执行或调用存储过程执行）
	 * @param cmdText
	 *            SQL语句或存储过程名称,SQL语句形式：(select * from T where Id=? and Name=? )
	 * @param obj
	 *            执行sql命令或存储过程时传入的可变参数
	 * @return （数据集合列表）返回：List<Map<String,Object>>
	 * @throws SQLException
	 */
	public  List queryForList(Connection conn, CommandType commandType, String cmdText, Object... obj)
			throws Exception {
		List list = Collections.EMPTY_LIST;;
		PreparedStatement preSm = null;
		CallableStatement cs = null;
		ResultSet resultSet = null;

		try {
			if (commandType.equals(CommandType.Text)) {
                preSm = prepareStatement(conn,cmdText,obj);
				resultSet = preSm.executeQuery();
			} else if (commandType.equals(CommandType.StoredProcedure)) {
                cs =  prepareCallableStatement(conn,cmdText,obj);
                resultSet = cs.executeQuery();
			}
			else if(commandType.equals(CommandType.LoadResourceFile)){
                preSm = prepareStatementFromResourceFile(conn,cmdText,getSqlFilePrefix(),getSqlFileSuffix(),obj);
                if(null != preSm)
                    resultSet = preSm.executeQuery();
            }

			list = resultSetToList(resultSet);
            log.info("执行数据库操作 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{}","queryForList",conn,commandType,cmdText, obj);
		} catch (SQLException e) {
			e.printStackTrace();
            log.error("执行数据库操作异常:{} 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{}",e.toString(),"queryForList",conn,commandType,cmdText, obj);
        } finally {
			close(conn, preSm, null, cs, resultSet);
		}
		return list;
	}

	/**
	 * 通过SQL查询语句、存储过程、本地sql模板文件得到数据集合列表
	 * 
	 * @param commandType
	 *            执行类型（SQL语句执行或调用存储过程执行）
	 * @param cmdText
	 *            SQL语句或存储过程名称,SQL语句形式：(select * from T where Id=? and Name=?)
	 * @param obj
	 *            执行sql命令或存储过程时传入的可变参数
	 * @return （数据集合列表）返回：List<Map<String,Object>>
	 */
	public  List queryForList(CommandType commandType, String cmdText, Object... obj) throws Exception {
		return queryForList(getConnection(), commandType, cmdText, obj);
	}

    /***
     * 通过SQL查询语句得到数据集合列表
     * @param cmdText 执行查询SQL语句
     * @param obj 执行sql命令或存储过程时传入的可变参数
     * @return 数据集合列表）返回：List<Map<String,Object>>
     * @throws Exception
     */
    public  List queryForList(String cmdText, Object... obj) throws Exception {
        return queryForList(getConnection(), CommandType.Text, cmdText, obj);
    }

    /**
     * 通过sql查询，并把结果集填充到对应类型的实体对象集合中。<BR/>
     * 填充规则：[实体属性名称与结果集字段 	  1.不分大小写  2.驼峰格式相互可以转成 相对应],找到便填充。
     * @param sql 查询的SQL语句 ;SQL语句形式：(select * from T where Id=? and Name=? 或者 select * from T where Id='1' and Name='A') <br/>
     * @param type 实体对象的类型（如：T.class）
     * @param args 查询可变参数
     * @return 实体对象集合
     * @throws Exception
     */
    public <T> List<T> queryForList(String sql,Class<T> type, Object... args) throws Exception {
        List<Map<String,Object>> list = this.queryForList(sql, args);
        return ConvertUtils.dataMapListToObjects(list, type);
    }

    /**
     * 通过SQL查询语句、存储过程、本地sql模板文件得到第一条查询结果数据,数据以Map形式返回
     * @param conn 数据库连接对象
     * @param commandType 执行类型（SQL语句执行或调用存储过程执行或者加载本地sql执行文件执行）
     * @param cmdText SQL语句或存储过程名称或者本地sql文件名,SQL语句形式：(select * from T where Id=? and Name=?)
     * @param obj 执行sql命令或存储过程时传入的可变参数
     * @return  第一条查询结果数据,数据以Map形式返回
     * @throws Exception
     */
	public  Map<String,Object> queryForMap(Connection conn, CommandType commandType, String cmdText, Object... obj) throws Exception {
        Map<String,Object> row = new HashMap<>();
        List<Map<String,Object>> rows = queryForList(conn,commandType,cmdText,obj);
        if(!rows.isEmpty() && rows != null){
            rows.get(0);
        }
        return row;
    }

    /**
     * 通过SQL查询语句、存储过程、本地sql模板文件得到第一条查询结果数据,数据以Map形式返回
     * @param commandType 执行类型（SQL语句执行或调用存储过程执行或者加载本地sql执行文件执行）
     * @param cmdText SQL语句或存储过程名称或者本地sql文件名,SQL语句形式：(select * from T where Id=? and Name=?)
     * @param obj 执行sql命令或存储过程时传入的可变参数
     * @return  第一条查询结果数据,数据以Map形式返回
     * @throws Exception
     */
    public  Map<String,Object> queryForMap(CommandType commandType, String cmdText, Object... obj) throws Exception {
	    return queryForMap(getConnection(), commandType, cmdText, obj);
    }

    /**
     * 通过SQL查询语句得到第一条查询结果数据,数据以Map形式返回
     * @param cmdText SQL查询语句
     * @param obj 执行sql时传入的可变参数
     * @return 第一条查询结果数据,数据以Map形式返回
     * @throws Exception
     */
    public  Map<String,Object> queryForMap(String cmdText, Object... obj) throws Exception {
        return queryForMap(getConnection(), CommandType.Text, cmdText, obj);
    }

    /**
     * 通过sql查询，并把结果集填充到对应类型的实体对象中。<BR/>
     * 填充规则：[实体属性名称与结果集字段 	  1.不分大小写  2.驼峰格式相互可以转成 相对应],找到便填充。
     * @param sql 查询的SQL语句 ;SQL语句形式：(select * from T where Id=? and Name=? 或者 select * from T where Id='1' and Name='A') <br/>
     * @param type 实体对象的类型（如：T.class）
     * @param args 查询可变参数
     * @return 单个实体对象
     * @throws Exception
     */
    public <T> T queryForObject(String sql,Class<T> type, Object... args) throws Exception {
        Map<String,Object> row = queryForMap(sql,args);
        return ConvertUtils.dataMapToObject(row,type);
    }


    /**
     * 执行指定数据库连接字符串的命令,返回结果集中的第一行第一列.
     * @param conn 数据库连接对象
     * @param commandType 执行类型（SQL语句执行或调用存储过程执行或者加载本地sql执行文件执行）
     * @param cmdText SQL语句或存储过程名称或者本地sql文件名,SQL语句形式：(select * from T where Id=? and Name=?)
     * @param obj  执行sql命令或存储过程时传入的可变参数
     * @return 结果集中的第一行第一列的值
     * @throws Exception
     */
    public Object queryForScalar(Connection conn, CommandType commandType, String cmdText, Object... obj) throws Exception {
        Object retval = null;
        Map<String,Object> row = queryForMap(commandType,cmdText,obj);
        if(null != row && row.size() >0){
            for(Map.Entry entry : row.entrySet()) {
                retval = entry.getValue();
                break;
            }
        }
        return retval;
    }

    /**
     * 执行指定数据库连接字符串的命令,返回结果集中的第一行第一列.
     * @param commandType 执行类型（SQL语句执行或调用存储过程执行或者加载本地sql执行文件执行）
     * @param cmdText SQL语句或存储过程名称或者本地sql文件名,SQL语句形式：(select * from T where Id=? and Name=?)
     * @param obj  执行sql命令或存储过程时传入的可变参数
     * @return 结果集中的第一行第一列的值
     * @throws Exception
     */
    public Object queryForScalar(CommandType commandType, String cmdText, Object... obj) throws Exception {
        return queryForScalar(getConnection(), commandType, cmdText, obj);
    }

    /**
     * 执行指定数据库连接字符串的命令,返回结果集中的第一行第一列.
     * @param cmdText 执行查询SQL语句
     * @param obj 执行查询SQL语句时传入的可变参数
     * @return 结果集中的第一行第一列的值
     * @throws Exception
     */
    public Object queryForScalar(String cmdText, Object... obj) throws Exception {
        return queryForScalar(getConnection(), CommandType.Text, cmdText, obj);
    }

    /**
     * 通过存储过程得到数据集合列表
     * @param conn 数据库连接对象
     * @param spName 存储过程名称
     * @param dbParmCollection 传入存储过程参数集合
     * @param ignoreParameter  是否忽略 传入存储过程参数数量是否要与存储过程中的参数数量一致;如果为TRUE: 调用存储过程的参数集合 数量可以少于数据库中存储过程定义的参数个数, 没有传的参数默认以null传入
     * @return
     * @throws Exception
     */
	public  List queryForListBySp(Connection conn, String spName, DBParameterCollection dbParmCollection, boolean ignoreParameter)
			throws Exception {
		CallableStatement cs = null;
		ResultSet resultSet = null;
		List list = null;
		String sql ="";

		try {
			if(ignoreParameter){
			    sql = prepareCallSQLStr(spName);
				cs = conn.prepareCall(sql);
				fillPreparePramsIgnore(cs,spName,dbParmCollection);
			}
			else{				
			    sql = prepareCallSQLStr(spName, dbParmCollection);
				cs = conn.prepareCall(sql);
				fillPreparePrams(cs, dbParmCollection);
			}
			resultSet = cs.executeQuery();
			list = resultSetToList(resultSet);
			setOutDBParameterValue(cs, dbParmCollection);
            log.info("执行数据库操作 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{} 是否忽略参数校验:{}","queryForListBySp",conn,CommandType.StoredProcedure,spName, dbParmCollection,ignoreParameter);
		} catch (Exception e) {
			e.printStackTrace();
            log.error("执行数据库操作异常:{} 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{} 是否忽略参数校验:{}",e.toString(),"queryForListBySp",conn,CommandType.StoredProcedure,spName, dbParmCollection,ignoreParameter);
		} finally {
			close(conn, null, null, cs, resultSet);
		}
		return list;
	}
	
	/**
	 * 通过存储过程得到数据集合列表
	 * 默认：传入调用存储过程参数数量要与存储过程中的参数数量一致 
	 * @param conn 数据库连接对象
	 * @param spName 存储过程名称
	 * @param dbParmCollection 传入存储过程参数集合
	 * @return
	 * @throws Exception
	 */
	public  List queryForListBySp(Connection conn, String spName, DBParameterCollection dbParmCollection)
			throws Exception {
		return  queryForListBySp(conn,spName,dbParmCollection,false);
		
	}
	
	/**
	 * 通过存储过程得到数据集合列表
	 * 
	 * @param spName
	 *            存储过程名称
	 * @param dbParmCollection
	 *            存储过程参数集合
	 * @return List（数据集合列表）{返回：List<Map<String,Object>>}
	 * @throws Exception
	 */
	public  List queryForListBySp(String spName, DBParameterCollection dbParmCollection) throws Exception {
		return queryForListBySp(getConnection(), spName, dbParmCollection);
	}
	
	/**
	 * 通过存储过程得到数据表集合,即返回的数据集 (Map形式返回 类似于 .NET平台中的 DataTable[]和DataSet)
	 * @param conn 数据库连接对象
	 * @param spName 存储过程名称
	 * @param dbParmCollection 存储过程参数集合
	 * @param ignoreParameter 是否忽略 传入存储过程参数数量是否要与存储过程中的参数数量一致;如果为TRUE: 调用存储过程的参数集合 数量可以少于数据库中存储过程定义的参数个数, 没有传的参数默认以null传入
	 * @return  Map形式的数据表集合  Map<表名, 表数据集合>
	 * @throws Exception
	 */
	public  Map<String, List> queryForDataSetBySp(Connection conn, String spName, DBParameterCollection dbParmCollection, boolean ignoreParameter)
			throws Exception {
		CallableStatement cs = null;
		ResultSet resultSet = null;
		String sql ="";
		Map<String, List> tables = new LinkedHashMap<String, List>();
		int tableIndex = 0;
		try {
			if(ignoreParameter){
			    sql = prepareCallSQLStr(spName);
				cs = conn.prepareCall(sql);
				fillPreparePramsIgnore(cs,spName,dbParmCollection);
			}
			else{				
			    sql = prepareCallSQLStr(spName, dbParmCollection);
				cs = conn.prepareCall(sql);
				fillPreparePrams(cs, dbParmCollection);
			}
			//resultSet = cs.executeQuery();
			if (cs.execute()) {
				do {
					resultSet = cs.getResultSet();
					tables.put("Table" + String.valueOf(tableIndex), resultSetToList(resultSet));
					tableIndex++;
					//resultSet.close();
				} while (cs.getMoreResults());
			}
			setOutDBParameterValue(cs, dbParmCollection);
            log.info("执行数据库操作 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{} 是否忽略参数校验:{}","queryForDataSetBySp",conn,CommandType.StoredProcedure,spName, dbParmCollection,ignoreParameter);
		} catch (Exception e) {
			e.printStackTrace();
            log.error("执行数据库操作异常:{} 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{} 是否忽略参数校验:{}",e.toString(),"queryForDataSetBySp",conn,CommandType.StoredProcedure,spName, dbParmCollection,ignoreParameter);
        } finally {
			close(conn, null, null, cs, resultSet);
		}
		return tables;
	}
	
	/**
	 * 通过存储过程得到数据表集合,即返回的数据集 (Map形式返回 类似于 .NET平台中的 DataTable[]和DataSet)
	 * @param conn 数据库连接对象
	 * @param spName 存储过程名称
	 * @param dbParmCollection 存储过程参数集合
	 * @return Map形式的数据表集合 Map<表名, 表数据集合>
	 * @throws Exception
	 */
	public  Map<String, List> queryForDataSetBySp(Connection conn, String spName, DBParameterCollection dbParmCollection)
			throws Exception {
		return  queryForDataSetBySp(conn,spName,dbParmCollection,false);
		
	}
	
	/**
	 * 通过存储过程得到数据表集合,即返回的数据集 (Map形式返回 类似于 .NET平台中的 DataTable[]和DataSet)
	 * @param spName 存储过程名称
	 * @param dbParmCollection 存储过程参数集合
	 * @return Map形式的数据表集合  Map<表名, 表数据集合>
	 * @throws Exception
	 */
	public  Map<String, List> queryForDataSetBySp(String spName, DBParameterCollection dbParmCollection)
			throws Exception {
		return  queryForDataSetBySp(getConnection(),spName,dbParmCollection);
		
	}
	
	
	/**
	 * 从数据库中返回多张表的集合,即返回的数据集 (支持一次执行多个查询语句，返回查询出的数据集 Map形式返回 类似于 .NET平台中的 DataTable[]和DataSet)
	 * 补充知识：Map:>> 1.HashMap 无序 
	 *                2.TreeMap 有序
	 * @param conn
	 *            数据库连接对象
	 * @param commandType
	 *            执行类型（SQL语句执行或调用存储过程执行）
	 * @param cmdText
	 *            SQL语句或存储过程名称
	 * @param obj
	 *            SQL语句形式为 (select * from T where Id=? and Name=?;select * from T2 where Id=?) 时对应的参数
	 *            或调用存储过程时对应的参数
	 * @return 返回多张表的 Map<表名,数据列表>
	 * @throws SQLException
	 */
    public  Map<String, List> queryForDataSet(Connection conn, CommandType commandType, String cmdText, Object... obj) throws Exception {
        PreparedStatement preSm = null;
        CallableStatement cs = null;
        ResultSet resultSet = null;
        //此处之所以使用TreeMap 是为了能使Map 按key排序
        Map<String, List> tables = new TreeMap<String, List>();
        int tableIndex = 0;
        try {
            if (commandType.equals(CommandType.Text)) {
                preSm = prepareStatement(conn,cmdText,obj);
                if (preSm.execute()) {
                    do {
                        resultSet = preSm.getResultSet();
                        tables.put("Table" + String.valueOf(tableIndex), resultSetToList(resultSet));
                        tableIndex++;
                        //preSm.close();
                    } while (preSm.getMoreResults());
                }
            } else if (commandType.equals(CommandType.StoredProcedure)) {
                cs = prepareCallableStatement(conn,cmdText,obj);
                if (cs.execute()) {
                    do {
                        resultSet = cs.getResultSet();
                        tables.put("Table" + String.valueOf(tableIndex), resultSetToList(resultSet));
                        tableIndex++;
                        //resultSet.close();
                    } while (cs.getMoreResults());
                }
            } else if (commandType.equals(CommandType.LoadResourceFile)) {
                preSm = prepareStatementFromResourceFile(conn,cmdText, getSqlFilePrefix(), getSqlFileSuffix(), obj);
                if (preSm.execute()) {
                    do {
                        resultSet = preSm.getResultSet();
                        tables.put("Table" + String.valueOf(tableIndex), resultSetToList(resultSet));
                        tableIndex++;
                        //preSm.close();
                    } while (preSm.getMoreResults());
                }
            }
            log.info("执行数据库操作 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{} ", "queryForDataSet", conn, commandType, cmdText, obj);
        } catch (SQLException e) {
            e.printStackTrace();
            log.error("执行数据库操作异常:{} 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{} ", e.toString(), "queryForDataSet", conn, commandType, cmdText, obj);
        } finally {
            close(conn, preSm, null, cs, resultSet);
        }

        return tables;
    }

	/**
	 * 从数据库中返回多张表的集合,即返回的数据集 (支持一次执行多个查询语句，返回查询出的数据集 Map形式返回 类似于 .NET平台中的 DataTable[]和DataSet)
	 * 
	 * @param commandType
	 *            执行类型（SQL语句执行或调用存储过程执行）
	 * @param cmdText
	 *            SQL语句或存储过程名称
	 * @param obj
	 *            SQL语句形式为 (select * from T where Id=? and Name=?) 时对应的参数
	 *            或调用存储过程时对应的参数
	 * @return 返回多张表的 Map<表名,数据列表>
	 * @throws SQLException
	 */
	public  Map<String, List> queryForDataSet(CommandType commandType, String cmdText, Object... obj) throws Exception {
		return queryForDataSet(getConnection(), commandType, cmdText, obj);
	}

	/**
	 * 从数据库中返回多张表的集合,即返回的数据集 (支持一次执行多个查询语句，返回查询出的数据集 Map形式返回 类似于 .NET平台中的 DataTable[]和DataSet)<br/>
	 * queryForDataSet()的2个重载方法得到指定表的数据列表
	 * 
	 * @param conn
	 *            数据库连接对象
	 * @param commandType
	 *            执行类型（SQL语句执行或调用存储过程执行）
	 * @param cmdText
	 *            SQL语句或存储过程名称
	 * @param obj
	 *            SQL语句形式为 (select * from T where Id=? and Name=?) 时对应的参数
	 *            或调用存储过程时对应的参数
	 * @return 返回多张表的 Map<表名,数据列表>
	 * @throws SQLException
	 */
	public  DBTablesCollection queryForTablesCollection(Connection conn, CommandType commandType, String cmdText, Object... obj) throws Exception {
		DBTablesCollection tablesCollection = new DBTablesCollection();
		Map<String, List> tables = queryForDataSet(conn, commandType, cmdText, obj);
		for (Map.Entry<String, List> entry : tables.entrySet()) {
			DBTable table = new DBTable();
			table.setTableName(entry.getKey());
			table.setData(entry.getValue());
			tablesCollection.add(table);
		}
		// 按表名排序输出表集合
		Collections.sort(tablesCollection, Comparator.comparing(DBTable::getTableName));
		return tablesCollection;
	}
	
	/**
	 * 从数据库中返回多张表的集合,即返回的数据集 (支持一次执行多个查询语句，返回查询出的数据集 Map形式返回 类似于 .NET平台中的 DataTable[]和DataSet)
	 * 返回的结果可以通过 queryForDataSet()的2个重载方法得到指定表的数据列表
	 * @param commandType   执行类型（SQL语句执行或调用存储过程执行）
	 * @param cmdText SQL语句或存储过程名称
	 * @param obj SQL语句形式为 (select * from T where Id=? and Name=?) 时对应的参数 或调用存储过程时对应的参数
	 * @return 返回多张表的 Map<表名,数据列表>
	 * @throws SQLException
	 */
	public  DBTablesCollection queryForTablesCollection(CommandType commandType, String cmdText, Object... obj) throws Exception {
		return queryForTablesCollection(getConnection(), commandType, cmdText, obj);
	}

	/**
	 * 通过存储过程执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL（数据定义语言）语句。
	 * 默认 传入存储过程参数数量要与存储过程中的参数数量保持一致 
	 * @param conn
	 *            数据库连接对象
	 * @param spName
	 *            存储过程名称
	 * @param dbParmCollection
	 *            调用存储过程的参数集合
	 * @return
	 * @throws Exception
	 */
	public  int executeNonQuery(Connection conn, String spName, DBParameterCollection dbParmCollection) throws Exception {
		return executeNonQuery(conn,spName,dbParmCollection,false);
	}
	
	/**
	 * 通过存储过程执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL（数据定义语言）语句。
	 * @param conn 数据库连接对象
	 * @param spName 存储过程名称
	 * @param dbParmCollection 调用存储过程的参数集合
	 * @param ignoreParameter  是否忽略 传入存储过程参数数量是否要与存储过程中的参数数量一致 ; 如果为TRUE: 调用存储过程的参数集合 数量可以少于数据库中存储过程定义的参数个数, 没有传的参数默认以null传入
	 * @return
	 * @throws Exception
	 */
	public  int executeNonQuery(Connection conn, String spName, DBParameterCollection dbParmCollection,boolean ignoreParameter) throws Exception {
		CallableStatement cs = null;
		String sql ="";
		int i = -1;
		try {
			 if(ignoreParameter){
				 sql = prepareCallSQLStr(spName);
				 cs = conn.prepareCall(sql);
				 fillPreparePramsIgnore(cs, spName, dbParmCollection);
			 }
			 else{
				sql = prepareCallSQLStr(spName, dbParmCollection);
				cs = conn.prepareCall(sql);
				fillPreparePrams(cs, dbParmCollection);
			 }
			i = cs.executeUpdate();
			setOutDBParameterValue(cs, dbParmCollection);//执行完后把输出参数填充到执行前准备好的对应参数
            log.info("执行数据库操作 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{} 是否忽略参数校验{}","executeNonQuery",conn,CommandType.StoredProcedure,spName,dbParmCollection,ignoreParameter);
		} catch (Exception e) {
			e.printStackTrace();
			i = -1;
            log.error("执行数据库操作异常:{} 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{} 是否忽略参数校验{}",e.toString(),"executeNonQuery",conn,CommandType.StoredProcedure,spName,dbParmCollection,ignoreParameter);
		} finally {
			close(conn, null, null, cs, null);
		}

		return i;
	}
	
	/**
	 * 通过存储过程执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL（数据定义语言）语句。
	 * @param spName 存储过程名称
	 * @param dbParmCollection 调用存储过程的参数集合
	 * @param ignoreParameter  是否忽略 传入存储过程参数数量是否要与存储过程中的参数数量一致 ; 如果为TRUE: 调用存储过程的参数集合 数量可以少于数据库中存储过程定义的参数个数, 没有传的参数默认以null传入
	 * @return
	 * @throws Exception
	 */
	public  int executeNonQuery(String spName, DBParameterCollection dbParmCollection,boolean ignoreParameter) throws Exception {
		return executeNonQuery(getConnection(), spName, dbParmCollection,ignoreParameter);
	}

	/**
	 * 通过存储过程执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL（数据定义语言）语句。
	 * 
	 * @param spName
	 *            存储过程名称
	 * @param dbParmCollection
	 *            调用存储过程的参数集合
	 * @return
	 * @throws Exception
	 */
	public  int executeNonQuery(String spName, DBParameterCollection dbParmCollection) throws Exception {
		return executeNonQuery(getConnection(), spName, dbParmCollection);
	}
	


	/**
	 * 通过预编译的SQL查询语句（防SQL注入）或存储过程执行 INSERT、UPDATE 或 DELETE 语句以及 SQL
	 * DDL（数据定义语言）语句。
	 *
	 * @param conn
	 *            数据库连接对象
	 * @param commandType
	 *            执行类型（SQL语句执行或调用存储过程执行）
	 * @param cmdText
	 *            SQL语句或存储过程名称,SQL语句形式：(insert into T(C1,C2,C3) values(?,?,?))
	 * @param obj
	 *            执行存储过程时传入的可变参数
	 * @return 返回执行sql操作所影响的行数 【定义 -1 为操作失败】
	 * @throws SQLException
	 */
	public  int excuteNonQuery(Connection conn, CommandType commandType, String cmdText, Object... obj) throws Exception {
		int i = -1;
		PreparedStatement preSm = null;
		CallableStatement cs = null;

		try {
			if (commandType.equals(CommandType.Text)) {
				preSm = prepareStatement(conn,cmdText,obj);
				i = preSm.executeUpdate();
			} else if (commandType.equals(CommandType.StoredProcedure)) {
				cs = prepareCallableStatement(conn,cmdText,obj);
				i = cs.executeUpdate();
			} else if(commandType.equals(CommandType.LoadResourceFile)){
                preSm = prepareStatementFromResourceFile(conn,cmdText,getSqlFilePrefix(),getSqlFileSuffix(),obj);
                i = preSm.executeUpdate();
            }
            log.info("执行数据库操作 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{}", "excuteNonQuerySecurity", conn, commandType, cmdText, obj);
		} catch (SQLException e) {
			e.printStackTrace();
            log.error("执行数据库操作异常:{} 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{}", e.toString(), "excuteNonQuerySecurity", conn, commandType, cmdText, obj);
		} finally {
			close(conn, preSm, null, cs, null);
		}

		return i;
	}

	/**
	 * 通过预编译的SQL查询语句（防SQL注入）或存储过程执行 INSERT、UPDATE 或 DELETE 语句以及 SQL
	 * DDL（数据定义语言）语句。
	 *
	 * @param commandType 执行类型（SQL语句执行或调用存储过程执行）
	 * @param cmdText SQL语句或存储过程名称,SQL语句形式：(insert into T(C1,C2,C3) values(?,?,?))
	 * @param obj 执行存储过程时传入的可变参数
	 * @return 返回执行sql操作所影响的行数 【定义 -1 为操作失败】
	 */
	public  int excuteNonQuery(CommandType commandType, String cmdText, Object... obj) throws Exception {
		return excuteNonQuery(getConnection(), commandType, cmdText, obj);
	}
	
	/**
	 * 向数据库中插入数据的同时返回其对应自动生成的ID
	 * @param conn 数据库连接对象
	 * @param cmdText 数据库插入语句 可以是 形式1：insert into T(C1,C2,C3) values(?,?,?) 形式2：insert into T(C1,C2,C3) values('xxx','xxx','xxx')
	 * @param obj 数据库参数 (可变, 执行形式2时 不填此参数)
	 * @return id
	 * @throws SQLException
	 */
    public  Object insertAndGetGeneratedId(Connection conn, String cmdText, Object... obj) throws SQLException {
        PreparedStatement preSm = null;
        ResultSet rs = null;
        int i = -1;
        Object resultId = null;
        try {
            preSm = conn.prepareStatement(cmdText, Statement.RETURN_GENERATED_KEYS);

            for (int j = 0; j < obj.length; j++) {
                preSm.setObject(j + 1, obj[j]);
            }
            i = preSm.executeUpdate();
            rs = preSm.getGeneratedKeys();
            if (rs.next()) {
                resultId = rs.getObject(1);
            }
            log.info("执行数据库操作 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{}", "insertAndGetGeneratedId", conn, CommandType.Text, cmdText, obj);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("执行数据库操作异常:{} 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{}", e.toString(), "insertAndGetGeneratedId", conn, CommandType.Text, cmdText, obj);
        } finally {
            close(conn, preSm, null, null, rs);
        }

        return resultId;
    }
	
	/**
	 * 向数据库中插入数据的同时返回其对应自动生成的ID
	 * @param cmdText 数据库插入语句
	 * @param obj 数据库参数
	 * @return id
	 * @throws SQLException
	 */
	public  Object insertAndGetGeneratedId(String cmdText, Object... obj) throws SQLException{
		return insertAndGetGeneratedId(getConnection(), cmdText, obj);
	}

	/**
	 * 执行数据库存储过程之前拼凑好执行的SQL语句（如： {call spName(?,?,?,?,?,?,?,...?)}）
	 * 
	 * @param spName ：存储过程名称
	 * @param dbParmCollection ：DBParameterCollection （准备好的数据库参数集合）
	 * @return
	 */
	public  String prepareCallSQLStr(String spName, DBParameterCollection dbParmCollection) {

		String sql = "call " + spName;
		String callParameters = "";

		for (int i = 0; i < dbParmCollection.size(); i++) {
			if (i != (dbParmCollection.size()) - 1) {
				callParameters = callParameters + "?,";
			} else {
				callParameters = callParameters + "?";
			}
		}
		sql = "{" + sql + "(" + callParameters + ")}";

		return sql;
	}

	/**
	 * 执行数据库存储过程之前拼凑好执行的SQL语句（如： {call spName(?,?,?,?,?,?,?,...?)}）
	 * 
	 * @param ：存储过程名称
	 * @param :执行存储过程传入的可变参数
	 * @return
	 */
	public  String prepareCallSQLStr(String spName, Object... obj) {

		String sql = "call " + spName;
		String callParameters = "";

		for (int i = 0; i < obj.length; i++) {
			if (i != (obj.length) - 1) {
				callParameters = callParameters + "?,";
			} else {
				callParameters = callParameters + "?";
			}
		}
		sql = "{" + sql + "(" + callParameters + ")}";
		return sql;
	}
	
	/**
	 * 执行数据库存储过程之前拼凑好执行的SQL语句（如： {call spName(?,?,?,?,?,?,?,...?)}）
	 * 从数据库中直接取对应存储过程的参数来构建sql语句
	 * @param spName 存储过程名称
	 * @return
	 * @throws Exception
	 */
	public  String prepareCallSQLStr(String spName) throws Exception{
		String sql = "call " + spName;
		String callParameters = "";
		
		DBParameterCollection spParmCollection = DBParameterCache.GetCachedParameterSet(getConnection(), spName);
		if(spParmCollection == null){
			spParmCollection = DBParameterCache.GetSpParameterSet(getConnection(), spName);
			DBParameterCache.CacheParameterSet(getConnection(), spName, spParmCollection);
		}

		for (int i = 0; i < spParmCollection.size(); i++) {
			if (i != (spParmCollection.size()) - 1) {
				callParameters = callParameters + "?,";
			} else {
				callParameters = callParameters + "?";
			}
		}
		sql = "{" + sql + "(" + callParameters + ")}";

		return sql;
	}

    /**
     * 解析sql到待执行的PreparedStatement中
     * @param conn 数据库连接对象
     * @param cmdText 待执行的sql语句
     * @param obj 执行参数列表
     * @return 待执行的PreparedStatement
     * @throws Exception
     */
    public  PreparedStatement prepareStatement(Connection conn,String cmdText,Object... obj) throws Exception {
        PreparedStatement preSm = null;
	    preSm = conn.prepareStatement(cmdText);
        for (int i = 0; i < obj.length; i++) {
            preSm.setObject(i + 1, obj[i]);
        }
        return preSm;
    }

    /**
     * 解析sql到待执行的CallableStatement中
     * @param conn 数据库连接对象
     * @param cmdText 待执行的sql语句(存储过程名称)
     * @param obj 执行参数列表
     * @return 待执行的CallableStatement
     * @throws Exception
     */
    public  CallableStatement prepareCallableStatement(Connection conn,String cmdText,Object... obj) throws Exception {
        CallableStatement cs = null;
        String sqlStr = prepareCallSQLStr(cmdText, obj);
        cs = conn.prepareCall(sqlStr);
        for (int i = 0; i < obj.length; i++) {
            cs.setObject(i + 1, obj[i]);
        }
        return cs;
    }

    /**
     * 从指定的sql文件中解析sql到待执行的PreparedStatement中
     * @param conn 数据库连接对象
     * @param cmdText 执行语句，通常为sql模板文件名称
     * @param filePrefix sql执行模板路径前缀
     * @param fileSuffix sql执行模板路径后缀（扩展名如.sql）
     * @param obj 执行参数列表
     * @return 待执行的PreparedStatement
     * @throws Exception
     */
	public  PreparedStatement prepareStatementFromResourceFile(Connection conn,String cmdText,String filePrefix,String fileSuffix,Object... obj) throws Exception {
        PreparedStatement preSm = null;
        ResolvedSqlResult result = ResolveSqlTemplate.resolveSql(cmdText,filePrefix,fileSuffix,obj);
        if(result.getResultCode().equals(ResolvedCodeType.Success)){
            preSm = conn.prepareStatement(result.getResultSqlScript().replaceAll("\r\n", "\n"));

            if(null != result.getParams()) {
                for (int i = 0; i < result.getParams().length; i++) {
                    preSm.setObject(i + 1, result.getParams()[i]);
                }
            }
        }
        return preSm;
    }
	

	/**
	 * 动态填充待执行sql语句参数
	 * @param cs ：待填充的 CallableStatement 对象
	 * @param dbParmCollection ：DBParameterCollection （准备好的数据库参数集合） 用户传人的参数集合
	 * @throws Exception
	 */
	public  void fillPreparePrams(CallableStatement cs, DBParameterCollection dbParmCollection) throws Exception {
		Object parameterValue = null;
		String parameterName = "";
		DBParameterDirection dir = null;
		int dbType = Types.INTEGER;
		for (int i = 0; i < dbParmCollection.size(); i++) {
			DBParameter dBParameter = (DBParameter) dbParmCollection.get(i);
			// 取得每一参数所设置的值和对应的描述
			parameterValue = dBParameter.getParameterValue();
			dir = dBParameter.getParameterDirection();
			dbType = dBParameter.getDbType();
			parameterName = dBParameter.getParameterName();//.toUpperCase();

			// 设置CallableStatement 对象参数
			// cs.setObject(i + 1, parameterValue);
			//System.out.println(cs.getMetaData()); 
			if(null != parameterValue && dbType == Types.TIMESTAMP) {//如果是时间类型的参数
				Date date=new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.UK).parse(parameterValue.toString());
				cs.setTimestamp(parameterName, new Timestamp(date.getTime()));
			}
			else {
				cs.setObject(parameterName, parameterValue);
			}
			if (dir.equals(DBParameterDirection.Output) || dir.equals(DBParameterDirection.InputOutput) ) {
				// cs.registerOutParameter(i + 1, dbType);
				cs.registerOutParameter(parameterName, dbType);
			}
		}
	}

	/**
	 * 动态填充待执行sql语句参数 （ 忽略大小写,下划线的数据库命名,传递参数的数量）方式
	 * 【防止在创建的DBParameter时给的名称和数据库定义的名称存在大小写区别时执行报错问题】
	 * @param conn 数据库连接对象
	 * @param cs   待填充的 CallableStatement 对象
	 * @param spName  存储过程名称
	 * @param dbParmCollection 用户传人的参数集合
	 * @throws Exception
	 */
	public  void fillPreparePramsIgnore(Connection conn,CallableStatement cs,String spName ,DBParameterCollection dbParmCollection,String dbParamPrefixIgnore) throws Exception{

		Object parameterValue = null;
		String parameterName = "",spParameterName="";
		DBParameterDirection dir = null;
		int dbType = Types.INTEGER;

		//得到数据库中实际定义的参数
		DBParameterCollection spParmCollection = DBParameterCache.GetCachedParameterSet(conn, spName);
		if(spParmCollection == null){
			spParmCollection = DBParameterCache.GetSpParameterSet(conn, spName);
			DBParameterCache.CacheParameterSet(conn, spName, spParmCollection);
		}

		for (int i = 0; i < spParmCollection.size(); i++) {
			DBParameter spParam = (DBParameter) spParmCollection.get(i);
			spParameterName = spParam.getParameterName();
			dir = spParam.getParameterDirection();
			dbType = spParam.getDbType();
			parameterValue = null;

			for (int j = 0; j < dbParmCollection.size(); j++) {
				DBParameter param = (DBParameter) dbParmCollection.get(j);
				parameterName = param.getParameterName();
				if (spParameterName.equalsIgnoreCase(parameterName)
						|| parameterName.equalsIgnoreCase(spParameterName.replace(dbParamPrefixIgnore, "").replace("_", ""))
						|| spParameterName.equalsIgnoreCase(dbParamPrefixIgnore + parameterName)) {
					parameterValue = param.getParameterValue();
					break;
				}

			}
			// 设置CallableStatement 对象参数
			// cs.setObject(i + 1, parameterValue);
			if(null != parameterValue && dbType == Types.TIMESTAMP) {//如果是时间类型的参数
				Date date=new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.UK).parse(parameterValue.toString());
				cs.setTimestamp(parameterName, new Timestamp(date.getTime()));
			}
			else {
				cs.setObject(parameterName, parameterValue);
			}			
			//cs.setObject(spParameterName, parameterValue);
			if (dir.equals(DBParameterDirection.Output) || dir.equals(DBParameterDirection.InputOutput)) {
				// cs.registerOutParameter(i + 1, dbType);
				cs.registerOutParameter(spParameterName, dbType);
			}
		}
	}
	
	/**
	 * 动态填充待执行sql语句参数 （ 忽略大小写,下划线的数据库命名,传递参数的数量）方式
	 * 【防止在创建的DBParameter时给的名称和数据库定义的名称存在大小写区别时执行报错问题】
	 * @param cs   待填充的 CallableStatement 对象
	 * @param spName  存储过程名称
	 * @param dbParmCollection 用户传人的参数集合
	 * @throws Exception
	 */
	public  void fillPreparePramsIgnore(CallableStatement cs,String spName ,DBParameterCollection dbParmCollection) throws Exception{
		fillPreparePramsIgnore(getConnection(),cs,spName,dbParmCollection,getDbParamterPrefix());
	}
	
	/**
	 * 动态填充待执行sql语句参数 （ 忽略大小写,下划线的数据库命名,传递参数的数量）方式
	 * 【防止在创建的DBParameter时给的名称和数据库定义的名称存在大小写区别时执行报错问题】
	 * Col_Name ColName COLNAME colname COL_NAME 这些会被匹配为一致
	 * 对fillPreparePrams方法的加强
	 * @param cs 待填充的 CallableStatement 对象
	 * @param dbParmCollection 用户传人的参数集合
	 * @param spParmCollection 数据库中定义的参数集合
	 * @throws SQLException
	 */
	public  void fillPreparePramsIgnore(CallableStatement cs, DBParameterCollection dbParmCollection,DBParameterCollection spParmCollection ) throws SQLException{
		Object parameterValue = null;
		String parameterName = "",spParameterName="";
		DBParameterDirection dir = null;
		int dbType = Types.INTEGER;
		
		for(int i=0;i<spParmCollection.size();i++){		
			DBParameter spParam = (DBParameter)spParmCollection.get(i);
			spParameterName =spParam.getParameterName();
			dir = spParam.getParameterDirection();
			dbType = spParam.getDbType();
			parameterValue = null;
			
			for(int j=0;j<dbParmCollection.size();j++){
				DBParameter param = (DBParameter)dbParmCollection.get(j);
				parameterName = param.getParameterName();
				if (spParameterName.equalsIgnoreCase(parameterName)
						|| parameterName.equalsIgnoreCase(spParameterName.replace("_", ""))) {
					parameterValue = param.getParameterValue();
					break;
				}
			}			
			// 设置CallableStatement 对象参数
			// cs.setObject(i + 1, parameterValue);
			cs.setObject(spParameterName, parameterValue);
			if (dir.equals(DBParameterDirection.Output) || dir.equals(DBParameterDirection.InputOutput) ) {
				// cs.registerOutParameter(i + 1, dbType);
				cs.registerOutParameter(spParameterName, dbType);
			}
		}
	
	}
	
	/**
	 * 准备待执行的SQL参数对象。
	 * @param procedureName 待调用的存储过程名称
	 * @param entity 对应的实体对象
	 * @return 准备好的参数数组
	 * @throws Exception
	 */
	public  DBParameterCollection prepareParameters(String procedureName, Object entity) throws Exception {
		DBParameterCollection paramsCollection = new DBParameterCollection();
		DBParameterCollection params = DBParameterCache.GetCachedParameterSet(this.getConnection(), procedureName);
		if (params == null) {
			params = DBParameterCache.GetSpParameterSet(this.getConnection(), procedureName);
			DBParameterCache.CacheParameterSet(this.getConnection(), procedureName, params);
		}
		String paraName = "";
		Iterator iterator = params.iterator();
		while (iterator.hasNext()) {
			DBParameter param = (DBParameter) iterator.next();
			paraName = param.getParameterName();
			if (param.getParameterDirection() == DBParameterDirection.Input) {
				if (getDbParamterPrefix().length() > 0
						&& param.getParameterName().toLowerCase().startsWith(getDbParamterPrefix().toLowerCase())) {
					// 解决MYSQL或ORACAL参数命名以"V_"为前缀时候造成匹配不了的问题
					paraName = param.getParameterName().substring(getDbParamterPrefix().length());
				}
				if (param.getParameterName().startsWith("@") || param.getParameterName().startsWith(":")
						|| param.getParameterName().startsWith("?")) {
					paraName = param.getParameterName().substring(1);
				}
				paraName = ReflectionUtils.getPropertyName(entity.getClass(), CUtils.underlineToCamel(paraName));// 防止实体定义的属性与数据库中定义的参数存在大小写或（数据库命名以下划线隔开命名）的区别

				if (paraName.isEmpty() || paraName.equals("")) {// 如果在实体中没有定义对应参数的属性
					paramsCollection.add(param);
					continue;
				}
				Object paraValue = ReflectionUtils.getFieldValue(entity, paraName);
				if(paraValue != null && !"".equals(paraValue))
					param.setParameterValue(paraValue);
			}
			paramsCollection.add(param);
		}
		return paramsCollection;
	}

	/**
	 * CallableStatement对象执行完存储过程后把数据库中对应的输出参数（Output参数）的值回写到对应的DBParameter中。
	 * @param cs CallableStatement 对象
	 * @param dbParmCollection 数据库参数集合
	 * @throws SQLException
	 */
	public  void setOutDBParameterValue(CallableStatement cs, DBParameterCollection dbParmCollection)
			throws SQLException {
		DBParameterDirection dir = null;
		String parameterName = "";

		for (int i = 0; i < dbParmCollection.size(); i++) {
			DBParameter dBParameter = (DBParameter) dbParmCollection.get(i);
			dir = dBParameter.getParameterDirection();
			parameterName = dBParameter.getParameterName();

			if (dir.equals(DBParameterDirection.Output) || dir.equals(DBParameterDirection.InputOutput)) {			
				//处理当数据库定义参数时加了前缀而传参时未加对应的前缀。实体属性通常不不会加前缀
				if(dbParamterPrefix.length() >0 && !parameterName.toLowerCase().startsWith(dbParamterPrefix.toLowerCase())){
					parameterName = dbParamterPrefix+parameterName;
				}				
				Object parameterValue = cs.getObject(parameterName);
				dBParameter.setParameterValue(parameterValue);
			}
		}
	}

	/**
	 * 把ResultSet转化为List数组
	 * @param rs ResultSet 数据集
	 * @return List数组
	 * @throws SQLException
	 */
	public  List resultSetToList(ResultSet rs) throws SQLException {
		if (rs == null)
			return Collections.EMPTY_LIST;
		ResultSetMetaData md = rs.getMetaData(); // 得到结果集(rs)的结构信息，比如字段数、字段名等
		int columnCount = md.getColumnCount(); // 返回此 ResultSet 对象中的列数
		List list = new ArrayList();
		Map rowData = null;
		while (rs.next()) {
			//rowData = new HashMap(columnCount);//无序输出
			//rowData = new TreeMap();//使用TreeMap使之排序输出 
			rowData = new LinkedHashMap();//顺序输出
			for (int i = 1; i <= columnCount; i++) {
				rowData.put(md.getColumnName(i).toLowerCase(), rs.getObject(i));
				}
			list.add(rowData);
		    //System.out.println("list:" + list.toString());
		}
		return list;
	}

	/**
	 * 判断查询结果集中是否存在某列
	 * @param rs 查询结果集
	 * @param columnName 列名
	 * @return true 存在; false 不存在
	 */
	public  boolean isExistColumn(ResultSet rs, String columnName) {
		try {
			if (rs.findColumn(columnName) > 0) {
				return true;
			}
		} catch (SQLException e) {
			return false;
		}

		return false;
	}

	//存储后台存储过程名称的 HashMap
    private  final ConcurrentHashMap cacheMap_spName = new ConcurrentHashMap();
	/**
	 * 判断数据库中是否存在指定名称的存储过程
	 * @param spName 指定存储过程名称
	 * @return true 存在; false 不存在
	 * @throws SQLException
	 */
	public  boolean isExitProcedure(String spName) throws SQLException {

	    if(cacheMap_spName.get(getConnection().getMetaData().getURL() + ":" + spName) != null)
            return true;

		boolean isExit = false;
		ResultSet rs = null;
		Connection conn = null;
		try {
			conn = getConnection();
			DatabaseMetaData dbMd = conn.getMetaData();
			rs = dbMd.getProcedures(null, null, spName);
			if (rs.next()) {
				isExit = true;
                cacheMap_spName.put(conn.getMetaData().getURL() + ":" + spName,spName);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			close(conn, null, null, null, rs);
		}

		return isExit;
	}

    public  int  execSqlFile(String resourceFilePath) throws Exception {
        Statement sm = null;
        Connection conn= null ;
        String cmdText ="";
        int exec = -1;
        try {
//            ArrayList<String> sqlList = readFileByLines(CUtils.loadClassPathResourceFile(resourceFilePath).getPath());
            cmdText = CUtils.loadClassPathResourceFileToString(resourceFilePath).replaceAll("\r\n", "\n").replaceAll("\t","");
            conn = getConnection();
            sm = conn.createStatement();
//            for(String subsql :sqlList){
//                sm.addBatch(subsql);
//            }
//            sm.executeBatch();
            sm.execute(cmdText);

            log.info("执行数据库操作 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{} ", "execSqlFile", conn, "批处理文件", cmdText, resourceFilePath);
            return 1;
            //exec = sm.executeUpdate(cmdText);
        } catch (SQLException e) {
            e.printStackTrace();
            log.error("执行数据库操作异常:{} 方法名称:{} 连接对象:{} 执行语句:{} ", e.toString(), "execSqlFile", conn, resourceFilePath);
        } finally {
            close(conn, null, sm, null, null);
        }
        return exec;
    }

    /**
     * 批量执行指定文件中的sql脚本
     * @param filePath 文件路径
     * @return
     * @throws Exception
     */
    public  int execBatchSqlFromFile(String filePath) throws Exception {
        Statement sm = null;
        Connection conn= null ;
        String cmdText ="";
        int exec = -1;
        try {
            ArrayList<String> sqlList = readFileByLines(filePath);
            for(String subsql :sqlList){
               sm.addBatch(subsql);
            }
            sm.executeBatch();
            log.info("执行数据库操作 方法名称:{} 连接对象:{} 执行类型:{} 执行语句:{} 参数列表:{} ", "execBatchSqlFromFile", conn, "批处理文件", cmdText, filePath);
            exec = 1;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("执行数据库操作异常:{} 方法名称:{} 连接对象:{} 执行语句:{} ", e.toString(), "execBatchSqlFromFile", conn, filePath);
        } finally {
            close(conn, null, sm, null, null);
        }
        return exec;
    }

    /**
     * 以行为单位读取文件，并将文件的每一行格式化到ArrayList中，常用于读面向行的格式化文件
     */
    private  ArrayList<String> readFileByLines(String filePath) throws Exception {
        ArrayList<String> listStr=new ArrayList<>();
        StringBuffer sb=new StringBuffer();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(
                new FileInputStream(filePath), "UTF-8"));
            String tempString = null;
            int flag=0;
            // 一次读入一行，直到读入null为文件结束
            while ((tempString = reader.readLine()) != null) {
                // 显示行号
                // System.out.println("line " + line + ": " + tempString);
                if(tempString.trim().equals(""))
                    continue;
                if(tempString.substring(tempString.length()-1).equals(";")){
                    if(flag==1){
                        sb.append(tempString);
                        listStr.add(sb.toString());
                        sb.delete(0,sb.length());
                        flag=0;
                    }
                    else
                        listStr.add(tempString);
                }else{
                    flag=1;
                    sb.append(tempString);
                }
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }

        return listStr;
    }

	/**
	 * 数据库對象关闭操作
	 * 
	 * @param conn 数据库连接对象
	 * @param ps   PreparedStatement
	 * @param sm   Statement
	 * @param cs   CallableStatement
	 * @param resultSet  ResultSet
	 * @throws SQLException
	 */
	public  void close(Connection conn, PreparedStatement ps, Statement sm, CallableStatement cs,
			ResultSet resultSet) throws SQLException {
		if (resultSet != null) {
			try {
				resultSet.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if (ps != null) {
			try {
				ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if (sm != null) {
			try {
				sm.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if (cs != null) {
			try {
				cs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

		if (conn != null && conn.getAutoCommit()) {//当前连接不为空 并且未启用事务
			try {
				conn.close();
				tl.remove();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

	}

}

